﻿#pragma once

// Inner Fire 游戏引擎库
// 上传BUFFER类
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类提供上传到默认缓冲区（或其它）的快捷操作。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-19

#include <dx_core/dx_core.h>

namespace ifire {
/*
 * UploadBuffer类，用于方便上传
 */
template <typename T> class UploadBuffer {
public:
  UploadBuffer(
      ID3D12Device* device, UINT element_count, bool is_constant_buffer)
      : is_constant_buffer_(is_constant_buffer) {
    // 计算需要的内存数量
    element_byte_size_ = sizeof(T);
    if (is_constant_buffer_) { // 常量缓冲区必须是256B对齐
      element_byte_size_ = (element_byte_size_ + 255) & ~255;
    }
    data_byte_size_ = element_byte_size_ * element_count;

    // 创建上传缓冲区
    CD3DX12_HEAP_PROPERTIES upload_props(D3D12_HEAP_TYPE_UPLOAD);
    auto upload_buffer_desc = CD3DX12_RESOURCE_DESC::Buffer(data_byte_size_);
    HASSERT(device->CreateCommittedResource(&upload_props, D3D12_HEAP_FLAG_NONE,
        &upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
        IID_PPV_ARGS(&upload_buffer_)));

    // 对缓冲区进行印射
    HASSERT(upload_buffer_->Map(
        0, nullptr, reinterpret_cast<void**>(&mapped_data_)));

    // 只要还会修改当前的资源，我们就无须取消印射。
    // 但是，在资源被GPU使用期间，我们千万不能向该资源进行写操作（所以得使用同步技术）
  }

  UploadBuffer(const UploadBuffer& rhs) = delete;
  UploadBuffer& operator=(const UploadBuffer& rhs) = delete;

  ~UploadBuffer() {
    if (upload_buffer_ != nullptr) {
      upload_buffer_->Unmap(0, nullptr);
    }
    mapped_data_ = nullptr;
  }

  ID3D12Resource* Resource() const { return upload_buffer_.Get(); }
  void CopyData(int element_index, const T& data) {
    memcpy(&mapped_data_[element_index * element_byte_size_], &data, sizeof(T));
  }

private:
  ComPtr<ID3D12Resource> upload_buffer_;
  BYTE* mapped_data_;

  UINT element_byte_size_;
  UINT data_byte_size_;
  bool is_constant_buffer_;
};

} // namespace ifire